package com.mercator.client.graph;

import java.util.Collection;
import java.util.HashMap;
import java.util.Random;

import org.vaadin.gwtgraphics.client.DrawingArea;
import org.vaadin.gwtgraphics.client.Group;
import org.vaadin.gwtgraphics.client.Line;
import org.vaadin.gwtgraphics.client.Shape;
import org.vaadin.gwtgraphics.client.animation.Animate;

public abstract class GNode<T extends Comparable<? super T>, N extends GNode<T, N,S>, S extends Shape> extends Node<T,N> implements
		Drawable {

	protected int x_pos,y_pos, tx_pos,ty_pos,speed,radius;
	protected DrawingArea canvas;
	public boolean isrPivot;
	protected String id;
	protected HashMap<N, Line> inedges, outedges;
	public Group reps;
	public S rep;
	public Animate ax,ay;
	protected boolean selected = false;
	// temp SpringNode stuff
	protected double dx, dy, edgedx, edgedy,repulsiondx,repulsiondy;
	// protected NodeCreator<? extends GeneSet,? extends GNode<? extends
	// GeneSet>> creator;
	private Random gen;
	protected boolean firstdraw,finished_animate;
	
	public GNode(DrawingArea canvas, T element) {
		super(element);
		this.canvas = canvas;
		inedges = new HashMap<N, Line>();
		outedges = new HashMap<N, Line>();
		radius = 50;
		reps = new Group();
		rep = this.getShape();
		reps.add(rep);
		gen = new Random();
		rep.setX(gen.nextInt(canvas.getWidth()));
		rep.setY(gen.nextInt(canvas.getHeight()));
		rep.setFillColor("green");
		rep.setStrokeColor("black");
		//canvas.add(reps);
		speed = 10000;
		this.tx_pos = 0;
		this.ty_pos = 0;
		this.dx = 0.0;
		this.dy = 0.0;
		this.edgedx = 0.0;
		this.edgedy = 0.0;
		this.repulsiondx = 0.0;
		this.repulsiondy = 0.0;
		firstdraw = true;
		finished_animate = false;
		// this.sinkEvents(Event.MOUSEEVENTS);
	}
	
	protected GNode(T element){
		super(element);
	}

	@Override
	public void moveTo(double x, double y) {
		this.tx_pos = (int) x;
		this.ty_pos = (int) y;
		animate();
		this.x_pos = tx_pos;
		this.y_pos = ty_pos;
		
		if(y > canvas.getHeight())
			canvas.setHeight((int)y + "px");
		if(x > canvas.getWidth())
			canvas.setWidth((int)x + "px");

	}

	@Override
	public void animate() {
		if (firstdraw){
			canvas.add(reps);
			firstdraw = false;
		}
		
		 new Animate(rep, "x", rep.getX(), this.tx_pos, speed).start();
		new Animate(rep, "y", rep.getY(), this.ty_pos, speed).start();
		
		for ( N node : inedges.keySet()) {
				Line l = inedges.get(node);
					new Animate(l, "x1", getCenterX(), getTargetCenterX(), speed)
							.start();
					new Animate(l, "y1", getCenterY(), getTargetCenterY(), speed)
							.start();
			}
		
		for ( N node : inedges.keySet()) {
			
				Line l = outedges.get(node);
					new Animate(l, "x2", getCenterX(), getTargetCenterX(), speed)
							.start();
					new Animate(l, "y2", getCenterY(), getTargetCenterY(), speed)
							.start();
			}
		}
	

	@Override
	public int compareTo(N o) {
		return element.compareTo(o.element);
	}

	@Override
	public void move() {
		this.animate();
	}

	public void setPos(int x, int y) {
		rep.setX(x);
		rep.setY(y);

		for (N node : adjacentNodes) {
			if (!inedges.isEmpty()) {
				Line l = inedges.get(node);
					l.setX1(x);
					l.setY1(y);
				
			}
			if (!outedges.isEmpty()) {
				 Line l = outedges.get(node);
					l.setX2(x);
					l.setY2(y);
				
			}
		}
	}

	public int getX() {
		return rep.getX();
	}

	public int getY() {
		return rep.getY();
	}

	public boolean isSelected() {
		return selected;
	}

	@Override
	public void connect(N n) {
		super.connect(n);
		if(!inedges.containsKey(n) && !n.outedges.containsKey(this)){
		    Line edge = new Line(this.getCenterX(), this.getCenterY(), n.getCenterX(), n.getCenterY());
			inedges.put(n, edge);
			n.outedges.put(getThis(), edge);
			reps.add(edge);
			canvas.bringToFront(rep);
			canvas.bringToFront(n.rep);
			}
	}
	
	@Override
	public void connectAll(Collection<N> ln) {
		 super.connectAll(ln);
		 for(N n : ln)
			 this.connect(n);
	}
	
	
	
	
	public void connect(N n, String color) {
		super.connect(n);
		if(!inedges.containsKey(n) && !n.outedges.containsKey(this)){
		    Line edge = new Line(this.getCenterX(), this.getCenterY(), n.getCenterX(), n.getCenterY());
		    edge.setStrokeColor(color);
		    inedges.put(n, edge);
			n.outedges.put(getThis(), edge);
			reps.add(edge);
			canvas.bringToFront(rep);
			canvas.bringToFront(n.rep);
			}	
		}

	@Override
	public boolean equals(Object o) {
		return this.element.equals(((N)o).element);
	}

	@Override
	public int hashCode() {
		return element.hashCode();
	}

	public void unselect() {
		rep.setFillColor("green");
		rep.setStrokeColor("black");
	}

	public abstract N getThis();
	protected void select(){
			canvas.bringToFront(reps);
			rep.setFillColor("Red");
	}
	
	public int getWidth(){
		return radius-10;
	}
	
	public int getHeight(){
		return radius-10;
	}
	
	protected int getCenterX(){
		return rep.getX() + getWidth()/2;
	}
	
	protected int getCenterY(){
		return rep.getY() + getHeight()/2;
	}
	
	protected int getTargetCenterX(){
		return tx_pos + getWidth()/2;
	}
	
	protected int getTargetCenterY(){
		return ty_pos + getHeight()/2;
	}
	protected abstract S getShape();

}